<template>
    <div class="user-info-head">
        <div class="student-photo-box"
            :style="{ height: `${props.height}px`, width: `${props.width}px`, borderRadius: `${props.radius}px` }"
            @click="editCropper" :class="{
                'solid-border-box': avatar,
                'no-bg-color': props.noBgcolor
            }">
            <img v-if="avatar" :src="avatar" class="avatar" :style="{ borderRadius: `${props.radius}px` }" />
            <transition name="fade" mode="out-in">
                <div class="model-img-box" v-if="avatar&&isPeviewDel">
                    <div class="mine-flex-box">
                        <el-icon @click.stop="showImage">
                            <View />
                        </el-icon>
                        <span></span>
                        <el-icon @click.stop="clearImages">
                            <Delete />
                        </el-icon>
                    </div>
                </div>
            </transition>
            <el-icon v-if="!avatar"
                :style="props.noBgcolor ? 'font-size: 22px;color: #ffffff;' : 'font-size: 22px;color: #999999;'">
                <Plus />
            </el-icon>
            <div v-if="!avatar && props.tips" style="margin-top: 5px;">{{ props.tips }}</div>
            <div class="closeIcon" v-if="avatar && showCloceIcon" @click.stop="clearImages">
                <el-icon :color="props.noBgcolor ? '#B52427' : '#999999'" size="18">
                    <CircleCloseFilled />
                </el-icon>
            </div>
            <div class="progressBox" v-if="props.needProsess && isShowProgress">
                <el-progress :stroke-width="8" style="height:88px;height:88px;" type="circle"
                    :percentage="progressNum" />
            </div>
        </div>
        <!-- 如果不用裁剪的时候 -->
        <el-upload class="uploaderBox" v-if="!props.needCropper" ref="uploaderRef" style="height: 1px;width: 0px;"
            :show-file-list="false" :http-request="requestUpload2" :before-upload="beforeUpload">
        </el-upload>
        <Model title="图片裁剪" v-if="props.needCropper" :modelShow="dialogVisible" width="40" :btnShow="false"
            @cancel="dialogVisible = false">
            <el-row style="margin-top: 20px;">
                <el-col :xs="24" :md="24" :style="{ height: '350px' }">
                    <VueCropper ref="cropperRef" :img="options.img" :outputType="options.outputType" :info="true"
                        :infoTrue="options.infoTrue" :auto-crop="options.autoCrop" :fixed-box="options.fixedBox"
                        :fixedNumber="props.fixedRate" :fixed="options.fixed" :centerBox="options.centerBox"
                        :autoCropWidth="props.autoCropWidth" :autoCropHeight="props.autoCropHeight" @realTime="realTime"
                        :full="options.full" :high="options.high" :cross-origin="'anonymous'">
                    </VueCropper>
                    <div class="plusAdd" v-if="!options.img">
                        <el-upload action="string" :show-file-list="false"
                            :http-request="requestUpload" :before-upload="beforeUpload">
                            <el-icon v-if="!avatar" style="font-size: 38px;color: #999999;">
                                <Plus />
                            </el-icon>
                            <div v-if="!avatar && props.tips" style="margin-top: 20px;width: 100%;font-size: 16px;">点击上传图片</div>
                        </el-upload>
                    </div>
                </el-col>
            </el-row>
            <el-row class="ptb2">
                <el-col :span="20">
                    <el-upload style="display:inline-block;margin-right: 12px;" action="string" :show-file-list="false"
                        :http-request="requestUpload" :before-upload="beforeUpload">
                        <el-button>
                            选择
                            <el-icon class="el-icon--right">
                                <Upload />
                            </el-icon>
                        </el-button>
                    </el-upload>
                    <el-button icon="Plus" @click="changeScale(1)"></el-button>
                    <el-button icon="Minus" @click="changeScale(-1)"></el-button>
                    <el-button icon="RefreshLeft" @click="rotateClick('left')"></el-button>
                    <el-button icon="RefreshRight" @click="rotateClick('right')"></el-button>
                    <el-button @click="refreshCrop()">重 置</el-button>
                </el-col>
                <el-col :span="4" style="text-align: right;">
                    <el-button type="primary" @click="uploadImg">提 交</el-button>
                </el-col>
            </el-row>
        </Model>
        <!-- 查看图片 -->
        <el-image-viewer v-if="showViewer" @close="closeViewer" :url-list="srcList"></el-image-viewer>
    </div>
</template>
<script lang="ts" setup>
import { ElMessage, UploadProps, UploadRequestOptions } from 'element-plus'
import { reactive, ref, toRefs, watch, onMounted } from "vue";
import VueCropper from "vue-cropper/src/vue-cropper.vue";
import { compress, compressAccurately } from 'image-conversion'
import * as imageConversion from 'image-conversion'; // 图片压缩
import upload from '../../util/OSSUpload'
// import utils from "@/utils/utils";
// 父级传过来的图片 interface
interface Props {
    avatar: string,
    width: number,
    height: number,
    dir: string,
    autoCropWidth: number,
    autoCropHeight: number,
    showCloceIcon: boolean,
    tips: string
    needCropper: boolean,
    radius: number,
    noBgcolor: boolean,
    needProsess: boolean, // 是否需要压缩
    isPeviewDel: boolean, // 是否可以预览删除
    fixedRate:any
}
// 父级传过来的图片
const props = withDefaults(defineProps<Props>(), {
    avatar: "",
    width: 100,
    height: 100,
    dir: '',
    autoCropWidth: 100,
    autoCropHeight: 100,
    showCloceIcon: true,
    tips: '',
    needCropper: true,
    radius: 4,
    noBgcolor: false,
    needProsess: false, // 是否需要压缩
    isPeviewDel:false,
    fixedRate: [1, 1]
});
// 转化
let { avatar } = toRefs(props);
// 裁剪相关配置类型 interface
interface Options {
    img: string | ArrayBuffer | null; // 裁剪图片的地址
    info: true; // 裁剪框的大小信息
    outputSize: number; // 裁剪生成图片的质量 [1至0.1]
    outputType: "png"; // 裁剪生成图片的格式
    canScale: boolean; // 图片是否允许滚轮缩放
    autoCrop: boolean; // 是否默认生成截图框
    autoCropWidth: number; // 默认生成截图框宽度
    autoCropHeight: number; // 默认生成截图框高度
    fixedBox: boolean; // 固定截图框大小 不允许改变
    fixed: boolean; // 是否开启截图框宽高固定比例
    // fixedNumber: Array<number>; // 截图框的宽高比例  需要配合centerBox一起使用才能生效
    full: boolean; // 是否输出原图比例的截图
    high: boolean;
    canMoveBox: boolean; // 截图框能否拖动
    original: boolean; // 上传图片按照原始比例渲染
    centerBox: boolean; // 截图框是否被限制在图片里面
    infoTrue: boolean; // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
}
// 裁剪相关配置
const options: Options = reactive({
    img: "", // 需要剪裁的图片
    autoCrop: true, // 是否默认生成截图框
    autoCropWidth: 1200, // 默认生成截图框的宽度
    autoCropHeight: 340, // 默认生成截图框的长度
    fixedBox: false, // 是否固定截图框的大小 不允许改变
    info: true, // 裁剪框的大小信息
    outputSize: 1, // 裁剪生成图片的质量 [1至0.1]
    outputType: "png", // 裁剪生成图片的格式
    canScale: false, // 图片是否允许滚轮缩放
    fixed: true, // 是否开启截图框宽高固定比例
    // fixedNumber: props.fixedRate, // 截图框的宽高比例 需要配合centerBox一起使用才能生效
    full: true, // 是否输出原图比例的截图
    high: true, //是否按照设备的dpr 输出等比例图片
    canMoveBox: false, // 截图框能否拖动
    original: false, // 上传图片按照原始比例渲染
    centerBox: true, // 截图框是否被限制在图片里面
    infoTrue: true, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
});
interface EmitType {
    (e: 'changeImage', obj?: any): void
    (e: 'clearImage'): void
}
const emit = defineEmits<EmitType>()
// 裁剪之后的数据
const previews: any = ref({});
// 获取图片裁剪实例
const cropperRef: any = ref({});
// 弹窗状态
const dialogVisible = ref<boolean>(false);
// 旋转图片
let rotateClick = (type: string) => {
    if (type == "left") {
        cropperRef.value.rotateLeft();
    }
    if (type == "right") {
        cropperRef.value.rotateRight();
    }
}
// 放大缩小图片比例
const changeScale = (num: number) => {
    const scale = num || 1;
    cropperRef.value.changeScale(scale);
}
// 重置图片
const refreshCrop = () => {
    cropperRef.value.refresh();
}
// 裁剪之后的数据
const realTime = (data: any) => {
    previews.value = data;
}
// 覆盖默认上传行为
let requestUpload = () => { };
const requestUpload2 = async (options: UploadRequestOptions) => {
    await yasuoFile(options.file)
}
// 上传预处理
const pngToWhiteBg = async (file: any, name: any) => {
    let read = new FileReader();
    read.readAsDataURL(file); // 文件转base64
    return new Promise((resolve, reject) => {
        read.onload = (e: any) => {
            let img: any = new Image();
            img.src = e.target.result;
            img.onload = async () => {
                // 生成canvas
                let canvas = document.createElement("canvas");
                let context: any = canvas.getContext("2d");
                // 绘制图片到canvas上
                canvas.width = img.width;
                canvas.height = img.height;

                // 在canvas绘制前填充白色背景
                context.fillStyle = "#fff";
                context.fillRect(0, 0, canvas.width, canvas.height);
                context.drawImage(img, 0, 0);
                let base64 = canvas.toDataURL(file["type"], 1);
                let newFile = await dataUrlToFile(base64, name);
                resolve(newFile);
            }
        }
    })
}
const dataUrlToFile = async (dataurl: any, filename: any) => {
    const arr = dataurl.split(',')
    const mime = arr[0].match(/:(.*?);/)[1]
    const bstr = atob(arr[1])
    let len = bstr.length
    const u8arr = new Uint8Array(len)
    while (len--) {
        u8arr[len] = bstr.charCodeAt(len)
    }
    return new File([u8arr], filename, { type: mime })
}
// 上传图片
const beforeUpload: UploadProps['beforeUpload'] = rawFile => {
    const typeList = ['image/jpeg', 'image/png']
    const isTypeValid = typeList.includes(rawFile.type)
    if (!isTypeValid) {
        ElMessage.error('请上传正确的图片!')
        return false
    }
    // if (!isLt2M) {
    //     Message.error('上传图片大小不能超过2M!')
    //     return false
    // }
    // 处理预上传的图片及格式
    const reader = new FileReader()
    reader.readAsDataURL(rawFile)
    reader.onload = () => {
        options.img = reader.result
    }
}
// 编辑头像
const uploaderRef = ref<any>(null)
const editCropper = () => {
    if (props.needCropper) {
        dialogVisible.value = true
        if (avatar.value) {
            ToBase64(avatar.value)
        } else {
            previews.value.url = ''
            options.img = ''
        }
    } else {
        if (uploaderRef.value) {
            uploaderRef.value.$el.querySelector('input').click()
        }
    }
}
// 图片转为base64格式
const ToBase64 = (imgUrl: any) => {
    // 一定要设置为let，不然图片不显示
    let image = new Image()
    // 解决跨域问题
    image.setAttribute('crossOrigin', 'anonymous)');
    image.src = `${imgUrl}?time=${new Date().valueOf()}` // src 加上时间戳
    image.onload = () => {
        let canvas = document.createElement('canvas');
        canvas.width = image.width;
        canvas.height = image.height;
        let context: any = canvas.getContext('2d');
        context.drawImage(image, 0, 0, image.width, image.height);
        let quality = 0.8;
        options.img = canvas.toDataURL('image/jpeg', quality)
        previews.value.url = canvas.toDataURL('image/jpeg', quality)
    }
}
// 提交
const uploadImg = async () => {
    if (!options.img) {
        return ElMessage.error('请选择上传文件!')
    }
    cropperRef.value.getCropData(async (data: any) => {
        let formData: any = await dataUrlToFile(data, 'fileName.jpg')
        if (formData.size / 1024 > 1000000) { // 大于 300 kb 就压缩
            let file: any = await pngToWhiteBg(formData, formData.name)
            let fileOfBlob = new File([file], formData.name, { type: file.type })
            compressAccurately(fileOfBlob, 200).then(async (res) => {
                let formfile = await blobToFile(res, formData.name, formData.type)
                await yasuoFile(formfile)
            })
        } else {
            let file: any = await pngToWhiteBg(formData, formData.name)
            let fileOfBlob = new File([file], formData.name, { type: file.type })
            await yasuoFile(fileOfBlob)
        }
    });
}
const progressNum = ref<number>(0)
const isShowProgress = ref<boolean>(false)
const yasuoFile = async (formData: any) => {
    isShowProgress.value = true
    progressNum.value = 0
    upload(formData, (res: any) => {
        dialogVisible.value = false
        emit('changeImage', res)
        isShowProgress.value = false
    },
        (error: any) => {
            isShowProgress.value = false
            console.log(error)
        },
        (percent: any) => {
            progressNum.value = percent.percent
            console.log(percent)
        }
    )
}
// Blob转File
const blobToFile = async (blob: any, fileName: any, type: any) => {
    return new File([blob], `${fileName}`, { type: type })
}
const clearImages = async () => {
    emit('clearImage')
}
// 查看图片
const srcList = ref<any>([])
const showViewer = ref(false)// 显示查看器
const showImage = () => {
    srcList.value = []
    srcList.value.push(props.avatar)
    showViewer.value = true
}
const closeViewer = () => {
    showViewer.value = false
}
onMounted(() => {
});
</script>

<style scoped lang="scss">
:deep(.vue-cropper){
    background: #ffffff!important;
    border: 1px dashed #dcdfe6!important;
    border-radius: 4px!important;
}
.plusAdd{
    position: absolute;
    top:0px;
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    cursor: pointer;
    flex-flow: column;
    text-align: center;
    :deep(.el-upload) {
        display: block!important;
    }
}
.progressBox {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0px;
    left: 0px;
    right: 0px;
    bottom: 0px;
    background: rgba(0, 0, 0, 0.6);
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;

    :deep(.el-progress) {
        height: 60px !important;
        width: 60px !important;
    }

    :deep(.el-progress-circle) {
        height: 60px !important;
        width: 60px !important;
    }

    :deep(.el-progress--circle .el-progress__text) {
        color: #ffffff;
    }
}

.student-photo-box {
    border: 1px dashed var(--el-border-color);
    border-radius: 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    padding: 4px;
    box-sizing: border-box;
    flex-flow: column;
    background: #F5F5F5;

    img {
        width: 100%;
        height: 100%;
        border-radius: 4px;
        object-fit: contain;
    }

    .closeIcon {
        position: absolute;
        top: -9px;
        right: -9px;
        cursor: pointer;
        z-index: 999;
    }

    .model-img-box {
        background: rgba(0, 0, 0, 0.5);
        width: 100%;
        height: 100%;
        position: absolute;
        cursor: pointer;
        top: 0;
        left: 0;
        border-radius: 4px;
        z-index: 9;
        display: flex;
        align-items: center;
        justify-content: center;
        color: #fff;

        i {
            color: #fff;
            font-size: 16px;
        }

        .mine-flex-box {
            display: flex;
            width: 65%;
            justify-content: space-between;
            align-items: center;

            .el-icon {
                transition: all 1s;
                font-weight: bold;
                font-size: 18px;
            }

            span {
                width: 1px;
                margin: 0 7px;
                height: 12px;
                background: rgba(255, 255, 255, 0.9);
                display: inline-block;
            }

            .el-icon:first-of-type:hover {
                transform: scale(1.2);
            }

            .el-icon:last-of-type:hover {
                transform: scale(1.2);
            }
        }

        .zoom-big-box {
            display: flex;
            align-items: center;
            transition: all 1s;
        }
    }
}

.student-photo-box.no-bg-color {
    background: rgba(0, 0, 0, 0.2);
}

.solid-border-box {
    border: 1px solid var(--el-border-color);
}

.avatar-upload-preview {
    position: absolute;
    top: 50%;
    transform: translate(50%, -50%);
    width: 175px;
    height: 245px;
    border-radius: 4px;
    box-shadow: 0 0 4px #ccc;
    overflow: hidden;
}

.user-info-head {
    position: relative;
    display: inline-block;
}

.img-circle {
    border-radius: 50%;
}

.img-lg {
    width: 120px;
    height: 120px;
    cursor: pointer;
}

.ptb2 {
    margin-top: 20px;
}

:deep(.el-dialog__body) {
    padding: 20px !important;
}
</style>